package org.nextlab.crs.serialize;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import org.javarosa.core.data.IDataPointer;
import org.javarosa.core.model.FormDef;
import org.javarosa.core.model.IAnswerDataSerializer;
import org.javarosa.core.model.IFormDataModel;
import org.javarosa.core.model.instance.DataModelTree;
import org.javarosa.core.model.instance.TreeElement;
import org.javarosa.core.model.utils.IDataModelSerializingVisitor;
import org.javarosa.core.services.transport.ByteArrayPayload;
import org.javarosa.core.services.transport.DataPointerPayload;
import org.javarosa.core.services.transport.IDataPayload;
import org.javarosa.core.services.transport.MultiMessagePayload;


/**
 * A visitor-esque class which walks a DataModelTree and constructs an XML document
 * containing its instance.
 *
 * The XML node elements are constructed in a depth-first manner, consistent with
 * standard XML document parsing.
 *
 * @author Clayton Sims
 *
 */
public class CRSXFormSerializingVisitor implements IDataModelSerializingVisitor {

	/** The XML document containing the instance that is to be returned */
	StringBuffer formData;

	/** The serializer to be used in constructing XML for AnswerData elements */
	IAnswerDataSerializer serializer;
	
	/** The schema to be used to serialize answer data */
	FormDef schema;	//not used
	
	Vector dataPointers;
	
	private void init() {
		formData = null;
		schema = null;
		dataPointers = new Vector();
	}

	public byte[] serializeDataModel(IFormDataModel model, FormDef formDef) throws IOException {
		init();
		this.schema = formDef;
		return serializeDataModel(model);
	}

	/*
	 * (non-Javadoc)
	 * @see org.javarosa.core.model.utils.IDataModelSerializingVisitor#serializeDataModel(org.javarosa.core.model.IFormDataModel)
	 */
	public byte[] serializeDataModel(IFormDataModel model) throws IOException {
		init();
		if(this.serializer == null) {
			this.setAnswerDataSerializer(new CRSXFormAnswerDataSerializer());
		}
		model.accept(this);
		if(formData != null) {
			return formData.toString().getBytes("UTF-8");
		}
		else {
			return null;
		}
	}
	
	public IDataPayload createSerializedPayload	(IFormDataModel model) throws IOException {
		init();
		if(this.serializer == null) {
			this.setAnswerDataSerializer(new CRSXFormAnswerDataSerializer());
		}
		model.accept(this);
		if(formData != null) {
			byte[] form = formData.toString().getBytes("UTF-8");
			if(dataPointers.size() == 0) {
				return new ByteArrayPayload(form, null, IDataPayload.PAYLOAD_TYPE_TEXT);
			}
			MultiMessagePayload payload = new MultiMessagePayload();
			payload.addPayload(new ByteArrayPayload(form, null, IDataPayload.PAYLOAD_TYPE_TEXT));
			Enumeration en = dataPointers.elements();
			while(en.hasMoreElements()) {
				IDataPointer pointer = (IDataPointer)en.nextElement();
				payload.addPayload(new DataPointerPayload(pointer));
			}
			return payload; 
		}
		else {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.javarosa.core.model.utils.IDataModelVisitor#visit(org.javarosa.core.model.IFormDataModel)
	 */
	public void visit(IFormDataModel dataModel) {
		if(dataModel instanceof DataModelTree) {
			this.visit((DataModelTree)dataModel);
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.javarosa.core.model.utils.ITreeVisitor#visit(org.javarosa.core.model.DataModelTree)
	 */
	public void visit(DataModelTree tree) {
		formData = new StringBuffer();
		TreeElement root = tree.getRoot();
		if (root != null) {
			serializeNode(root);
		}
			
	}

	public void serializeNode (TreeElement instanceNode) {		
		if (instanceNode != null) {
			if(instanceNode.isLeaf()) {
				formData.append(instanceNode.getName() + "=");
				if(serializer.serializeAnswerData(instanceNode.getValue()) != null)
					formData.append(""+serializer.serializeAnswerData(instanceNode.getValue()));
			}
			else {
				//formData.append("<"+instanceNode.getName()+">");
				
				for (int i = 0; i < instanceNode.getNumChildren(); i++) {
					TreeElement child = (TreeElement)instanceNode.getChildren().elementAt(i);
					if (child != null) {
						serializeNode(child);
						if(child.isLeaf())
							formData.append("&");
					}
				}
				
				//formData.append("</"+instanceNode.getName()+">");
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.javarosa.core.model.utils.IDataModelSerializingVisitor#setAnswerDataSerializer(org.javarosa.core.model.IAnswerDataSerializer)
	 */
	public void setAnswerDataSerializer(IAnswerDataSerializer ads) {
		this.serializer = ads;
	}
}
